/**
 * 
 */
package org.neogenesis.web.dao;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.enterprise.context.SessionScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.xml.parsers.ParserConfigurationException;

import org.neogenesis.web.Configurations;
import org.neogenesis.web.Utils;

import org.neogenesis.web.loader.MetaPlatform;
import org.neogenesis.web.loader.MetaProbe;
import org.neogenesis.web.loader.MetaSample;
import org.neogenesis.web.loader.ProbesLoader;
import org.neogenesis.web.loader.arrayExpressLoader.ArrayExpressLoader;
import org.neogenesis.web.model.Experiment;
import org.neogenesis.web.model.Gene;
import org.neogenesis.web.model.Organism;
import org.neogenesis.web.model.Platform;
import org.neogenesis.web.model.Sample;
import org.neogenesis.web.model.TypePlatform;
import org.neogenesis.web.qualifier.DataRepository;
import org.xml.sax.SAXException;

/**
 * 
 */
@SessionScoped
public class ArrayExpressDAO implements Serializable {

	@Inject
	@DataRepository
	private EntityManager em;

	@Inject
	private PlatformDAO platformDAO;

	@Inject
	private GeneDAO geneDAO;

	@Inject
	private NcbiDAO ncbiDAO;

	@Inject
	private OrganismDAO organismDAO;

	@Inject
	private TypePlatformDAO typePlatformDAO;

	@Inject
	private SampleDAO sampleDAO;

	@Inject
	private ExperimentDAO experimentDAO;

	private Logger log;

	private String webAppPath;

	private ArrayExpressLoader loader;

	private Experiment experiment;

	private String fileAExpPath;

	private String folderAExpPath;

	private Map<String, MetaPlatform> metaPlatforms;

	public ArrayExpressDAO() {

	}

	public boolean init(String fileName) throws IOException {

		log = Logger.getLogger(this.getClass().toString());

		loader = new ArrayExpressLoader(fileName);

		this.webAppPath = FacesContext.getCurrentInstance()
				.getExternalContext().getRealPath("/");

		return true;
	}

	// carica l'esperimento, se nel DB è già presente ritorna false altrimenti
	// lo salva nel db e ritorna true
	public boolean loadExperiment() throws IOException,
			ParserConfigurationException, SAXException {

		experiment = loader.getExperiment();

		if (experimentDAO.findByName(experiment.getSeriesIid()) != null)
			return false;

		experimentDAO.create(experiment);
		return true;
	}

	public boolean loadPlatforms() throws ParserConfigurationException,
			SAXException, IOException {
		// controllo quante piattaforme sono associate a questo exp
		// controllo se sono gia presenti nel db
		// le carico in memoria e le salvo nel db

		Platform platform;
		MetaPlatform metaPlatform;

		metaPlatforms = loader.getPlatforms();

		for (String platformName : metaPlatforms.keySet()) {

			metaPlatform = metaPlatforms.get(platformName);
			platform = platformDAO.findByName(platformName);

			if (platform != null) {
				metaPlatform.setPlatform(platform);
				// load the map probe id gene id
				Map<String, Integer> map = platformDAO
						.getMapProbeIdGeneId(platform);
				metaPlatform.setMapProbeIdGeneID(map);

			} else {
				// ?? è gia fatto primadell'if ??
				// metaPlatform = metaPlatforms.get(platformName);

				platform = metaPlatform.getPlatform();

				// check if organism and TypePlatform exist
				Organism organism = organismDAO.findByName(platform
						.getOrganism().getName());

				if (organism != null) {
					platform.setOrganism(organism);
				} else if (platform.getOrganism().getName() == null) {
					platform.setOrganism(organismDAO.findByName("Homo Sapiens"));
				}

				TypePlatform typePlatform = typePlatformDAO.findByName(platform
						.getTypePlatform().getName());

				if (typePlatform != null) {
					platform.setTypePlatform(typePlatform);
				} else if (platform.getTypePlatform().getName() == null) {
					platform.setTypePlatform(typePlatformDAO
							.findByName("IN_SITU_OLIGONUCLEOTIDE"));
				}

				metaPlatform.setPlatform(platform);

				loadProbes(metaPlatform);

				platformDAO.create(platform);
				Logger.getLogger(GEOSeriesDAO.class.toString()).log(
						Level.INFO,
						"inserted platoform with name "
								+ platform.getAccessionName());
			}

		}

		return true;
	}

	private void loadProbes(MetaPlatform metaPlatform) throws IOException {
		// load the probes
		ProbesLoader probesLoader = loader.getProbesLoader(metaPlatform
				.getPlatform().getAccessionName());

		metaPlatform.setProbesLoader(probesLoader);
		metaPlatform.setType(probesLoader.getTypeMetaPlatform());

		switch (metaPlatform.getType()) {
		case NONE:

			break;
		case GENE_SYMBOL:
			loadProbesByGeneSymbol(metaPlatform);
			break;
		default:
			// the default is gene id
			loadProbesByGeneId(metaPlatform);
			break;
		}
	}

	private void loadProbesByGeneSymbol(MetaPlatform metaPlatform) {
		// TODO Auto-generated method stub

	}

	private void loadProbesByGeneId(MetaPlatform mPlatform) throws IOException {
		Gene gene = null;

		ProbesLoader probesLoader = mPlatform.getProbesLoader();

		String platformProbesPath = webAppPath + Configurations.platformsPath
				+ Utils.getUniqueNamePlatform(mPlatform.getPlatform());

		List<MetaProbe> toQuery = new LinkedList<MetaProbe>();
		Map<String, Integer> mapProbeIDGeneID = new HashMap<String, Integer>();

		// open the stream to the file
		BufferedWriter out = new BufferedWriter(new FileWriter(
				platformProbesPath));

		for (MetaProbe metaProbe : probesLoader) {
			String line = "";

			if (metaProbe.getGeneId().length() == 0)
				continue;

			gene = geneDAO.find(Integer.parseInt(metaProbe.getGeneId()));
			if (gene == null)
				toQuery.add(metaProbe);
			else {

				String probeId = metaProbe.getProbeId();
				int geneId = gene.getId();
				line = probeId + Configurations.FIELD_SEPARATOR + geneId + "\n";
				out.write(line);
				mapProbeIDGeneID.put(probeId, geneId);
			}
		}

		if (toQuery.size() > 0) {
			ncbiDAO.setBatchSize(1000);
			ncbiDAO.retrieveGenesById(toQuery);
			for (MetaProbe metaProbe : toQuery) {
				String line = "";

				if (metaProbe.getGeneId().length() == 0)
					continue;

				if (metaProbe.getGene() == null)
					continue;

				gene = geneDAO.find(metaProbe.getGene().getId());

				if (gene == null) {
					gene = metaProbe.getGene();
					geneDAO.create(gene);
				}

				String probeId = metaProbe.getProbeId();
				int geneId = gene.getId();
				line = probeId + Configurations.FIELD_SEPARATOR + geneId + "\n";
				out.write(line);
				mapProbeIDGeneID.put(probeId, geneId);
			}
		}
		out.flush();
		out.close();
		Logger.getLogger(GEOSeriesDAO.class.toString()).log(
				Level.INFO,
				"add the probe to platform "
						+ mPlatform.getPlatform().getAccessionName());
		// set the file with the probes
		mPlatform.getPlatform().setProbeFile(platformProbesPath);
		mPlatform.setMapProbeIdGeneID(mapProbeIDGeneID);
	}

	public boolean loadSamples() throws IOException,
			ParserConfigurationException, SAXException, InterruptedException {
		Sample sample;
		Platform platform;
		MetaPlatform metaPlatform;
		Map<String, Integer> mapProbeIdGeneId;

		// load the samples
		List<MetaSample> metaSamples = loader.getSamples(metaPlatforms);

		this.folderAExpPath = loader.getZipFolderPath();
		this.fileAExpPath = FacesContext.getCurrentInstance()
				.getExternalContext().getRealPath("/")
				+ Configurations.uploadPath + loader.getZipFileName();

		em.getTransaction().begin();

		for (MetaSample metaSample : metaSamples) {

			try {
				// get the reference platform
				metaPlatform = loader.getPlatforms().get(
						metaSample.getPlatformNameRef());
				platform = metaPlatform.getPlatform();

				mapProbeIdGeneId = metaPlatform.getMapProbeIdGeneID();

				sample = metaSample.getSample();
				sample.setExperiment(experiment);
				sample.setPlatformId(platform.getId());

				String sampleFilePath = webAppPath + Configurations.samplesPath
						+ Utils.getUniqueNameSample(sample);

				BufferedWriter file = new BufferedWriter(new FileWriter(
						sampleFilePath));

				// load the probe-has-sample in the db with the observation
				// value
				for (String[] ob : metaSample) {
					if (ob.length == 0)
						continue;
					// get the values
					String probeId = ob[0];
					double intensity = 1f;
					try {
						intensity = Double.parseDouble(ob[1]);
					} catch (NumberFormatException e) {
						System.out.println(ob[1]);
					}
					Integer geneId = mapProbeIdGeneId.get(probeId);
					// add to the buffer
					if (geneId != null) {
						String row = geneId + "\t" + intensity + "\n";
						file.write(row);
					}
				}
				file.flush();
				file.close();
				sample.setObservationFile(sampleFilePath);
				// add the sample to the db
				sampleDAO.create(sample);

				Logger.getLogger(GEOSeriesDAO.class.toString()).log(Level.INFO,
						"inserted sample " + sample.getName());

			} catch (Exception e) {
				em.getTransaction().rollback();
				log.log(Level.SEVERE, e.getMessage());
				return false;
			}

		}
		em.getTransaction().commit();
		return true;
	}

	public void deleteFiles() {
		Logger.getLogger(fileAExpPath + "removed: " + Utils.deleteFile(fileAExpPath));
		Utils.deleteFolder(folderAExpPath);

	}

}
